package com.white.progressview;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ProgressBar;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.app.Context;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Author White
 * Date 2017/4/23
 * Time 14:21
 */
public class HorizontalProgressView extends ProgressBar {
    /**
     * * The constant TOP
     */
    public static final int TOP = 1;
    /**
     * * The constant CENTRE
     */
    public static final int CENTRE = 0;
    /**
     * * The constant BOTTOM
     */
    public static final int BOTTOM = -1;
    /**
     * * The constant HorizontalProgressView_progressNormalColor
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSNORMALCOLOR = "progressNormalColor";
    /**
     * * The constant HorizontalProgressView_progressReachColor
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSREACHCOLOR = "progressReachColor";
    /**
     * * The constant HorizontalProgressView_progressTextColor
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTCOLOR = "progressTextColor";
    /**
     * * The constant HorizontalProgressView_progressTextSize
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTSIZE = "progressTextSize";
    /**
     * * The constant HorizontalProgressView_progressTextOffset
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTOFFSET = "progressTextOffset";
    /**
     * * The constant HorizontalProgressView_progressNormalSize
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSNORMALSIZE = "progressNormalSize";
    /**
     * * The constant HorizontalProgressView_progressReachSize
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSREACHSIZE = "progressReachSize";
    /**
     * * The constant HorizontalProgressView_progressTextPosition
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTPOSITION = "progressTextPosition";
    /**
     * * The constant HorizontalProgressView_progressTextVisible
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTVISIBLE = "progressTextVisible";
    /**
     * * The constant HorizontalProgressView_progressTextSkewX
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTSKEWX = "progressTextSkewX";
    /**
     * * The constant HorizontalProgressView_progressTextPrefix
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTPREFIX = "progressTextPrefix";
    /**
     * * The constant HorizontalProgressView_progressTextSuffix
     */
    public static final String HORIZONTALPROGRESSVIEW_PROGRESSTEXTSUFFIX = "progressTextSuffix";
    /**
     * * The constant TAG
     */
    private static final String TAG = HorizontalProgressView.class.getSimpleName();
    /**
     * * The constant STATE
     */
    private static final String STATE = "state";
    /**
     * * The constant NORMAL_BAR_SIZE
     */
    private static final String NORMAL_BAR_SIZE = "normal_bar_size";
    /**
     * * The constant NORMAL_BAR_COLOR
     */
    private static final String NORMAL_BAR_COLOR = "normal_bar_color";
    /**
     * * The constant REACH_BAR_SIZE
     */
    private static final String REACH_BAR_SIZE = "reach_bar_size";
    /**
     * * The constant REACH_BAR_COLOR
     */
    private static final String REACH_BAR_COLOR = "reach_bar_color";
    /**
     * * The constant TEXT_COLOR
     */
    private static final String TEXT_COLOR = "text_color";
    /**
     * * The constant TEXT_SIZE
     */
    private static final String TEXT_SIZE = "text_size";
    /**
     * * The constant TEXT_SUFFIX
     */
    private static final String TEXT_SUFFIX = "text_suffix";
    /**
     * * The constant TEXT_PREFIX
     */
    private static final String TEXT_PREFIX = "text_prefix";
    /**
     * * The constant TEXT_OFFSET
     */
    private static final String TEXT_OFFSET = "text_offset";
    /**
     * * The constant TEXT_POSITION
     */
    private static final String TEXT_POSITION = "text_position";
    /**
     * * The constant TEXT_VISIBLE
     */
    private static final String TEXT_VISIBLE = "text_visible";
    /**
     * * The constant TEXT_SKEW_X
     */
    private static final String TEXT_SKEW_X = "text_skew_x";
    /**
     * The constant M normal bar size
     */
    private int mNormalBarSize = Utils.dp2px(getContext(), 2);
    /**
     * The constant M normal bar color
     */
    private int mNormalBarColor = Color.getIntColor("#FFD3D6DA");
    /**
     * The constant M reach bar size
     */
    private int mReachBarSize = Utils.dp2px(getContext(), 2);
    /**
     * The constant M reach bar color
     */
    private int mReachBarColor = Color.getIntColor("#108ee9");
    /**
     * The constant M text size
     */
    private int mTextSize = Utils.sp2px(getContext(), 14);
    /**
     * The constant M text color
     */
    private int mTextColor = Color.getIntColor("#108ee9");
    /**
     * The constant M text offset
     */
    private int mTextOffset = Utils.dp2px(getContext(), 6);
    /**
     * The constant M background color
     */
    private int mBackgroundColor = Color.WHITE.getValue();
    /**
     * The constant M progress position
     */
    private int mProgressPosition = CENTRE;
    /**
     * The constant M text visible
     */
    private boolean mTextVisible = true;
    /**
     * The constant M text skew x
     */
    private float mTextSkewX;
    /**
     * The constant M text prefix
     */
    private String mTextPrefix = "";
    /**
     * The constant M text suffix
     */
    private String mTextSuffix = "%";
    /**
     * The constant M text paint
     */
    private Paint mTextPaint;
    /**
     * The constant M normal paint
     */
    private Paint mNormalPaint;
    /**
     * The constant M reach paint
     */
    private Paint mReachPaint;
    /**
     * 经过测量后得到的需要绘制的总宽度
     */
    private int mDrawWidth;
    /**
     * Upgrade SDK 5 to adapt to the onRefresh and onDraw sequences.
     */
    private boolean mIsOnRefreshed = false;

    /**
     * Horizontal progress view
     *
     * @param context context
     */
    public HorizontalProgressView(Context context) {
        this(context, null);
    }


    /**
     * Horizontal progress view
     *
     * @param context context
     * @param attrs   attrs
     */
    public HorizontalProgressView(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    /**
     * Horizontal progress view
     *
     * @param context      context
     * @param attrs        attrs
     * @param defStyleAttr def style attr
     */
    public HorizontalProgressView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        obtainAttributes(attrs);
        // 初始化画笔
        initPaint();
        setLayoutRefreshedListener(this::onRefreshed);
        addDrawTask(this::onDraw);
    }

    /**
     * On draw *
     *
     * @param component component
     * @param canvas    canvas
     */
    private void onDraw(Component component, Canvas canvas) {
        if (!mIsOnRefreshed) {
            onRefreshed(component);
            mIsOnRefreshed = true;
        }
        canvas.save();
        canvas.drawColor(mBackgroundColor, Canvas.PorterDuffMode.SRC);
        drawHorizontalProgressView(canvas);
        canvas.restore();
    }

    /**
     * On refreshed *
     *
     * @param component component
     */
    private void onRefreshed(Component component) {
        int widthSize = getWidth();
        int textHeight = (int) (mTextPaint.descent() - mTextPaint.ascent());
        int heightSize = Math.max(Math.max(mNormalBarSize, mReachBarSize), Math.abs(textHeight * 2))
                + getPaddingTop()
                + getPaddingBottom();
        heightSize = getHeight();
        // 实际绘制宽度
        mDrawWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        Element backgroundElement = getBackgroundElement();
        if (backgroundElement != null) {
            if (backgroundElement instanceof ShapeElement) {
                RgbColor[] rgbColors = ((ShapeElement) backgroundElement).getRgbColors();
                mBackgroundColor = rgbColors[0].asArgbInt();
            }
        }
    }

    /**
     * Init paint
     */
    protected void initPaint() {
        mTextPaint = new Paint();
        mTextPaint.setColor(new Color(mTextColor));
        mTextPaint.setStyle(Paint.Style.FILL_STYLE);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setAntiAlias(true); // 抗锯齿

        mNormalPaint = new Paint();
        mNormalPaint.setColor(new Color(mNormalBarColor));
        mNormalPaint.setStyle(Paint.Style.FILL_STYLE);
        mNormalPaint.setAntiAlias(true);
        mNormalPaint.setStrokeWidth(mNormalBarSize);

        mReachPaint = new Paint();
        mReachPaint.setColor(new Color(mReachBarColor));
        mReachPaint.setStyle(Paint.Style.FILL_STYLE);
        mReachPaint.setAntiAlias(true);
        mReachPaint.setStrokeWidth(mReachBarSize);
    }

    /**
     * Set progress value *
     *
     * @param progress progress
     */
    @Override
    public void setProgressValue(int progress) {
        super.setProgressValue(progress);
        invalidate();
    }

    /**
     * 获取自定义属性值
     *
     * @param attrs attrs
     */
    protected void obtainAttributes(AttrSet attrs) {
        mNormalBarSize = AttrUtils.getDimensionFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSNORMALSIZE, mNormalBarSize);
        mNormalBarColor = AttrUtils.getColorFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSNORMALCOLOR, mNormalBarColor);
        mReachBarSize = AttrUtils.getDimensionFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSREACHSIZE, mReachBarSize);
        mReachBarColor = AttrUtils.getColorFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSREACHCOLOR, mReachBarColor);
        mTextSize = AttrUtils.getDimensionFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTSIZE, mTextSize);
        mTextColor = AttrUtils.getColorFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTCOLOR, mTextColor);
        mTextSkewX = AttrUtils.getDimensionFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTSKEWX, 0);
        mTextSuffix = AttrUtils.getStringFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTSUFFIX, mTextSuffix);
        mTextPrefix = AttrUtils.getStringFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTPREFIX, mTextPrefix);
        mTextOffset = AttrUtils.getDimensionFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTOFFSET, mTextOffset);
        mProgressPosition = AttrUtils.getIntFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTPOSITION, mProgressPosition);
        mTextVisible = AttrUtils.getBooleanFromAttr(attrs, HORIZONTALPROGRESSVIEW_PROGRESSTEXTVISIBLE, mTextVisible);
    }

    /**
     * Draw horizontal progress view *
     *
     * @param canvas canvas
     */
    private void drawHorizontalProgressView(Canvas canvas) {
        canvas.translate(getPaddingLeft(), getHeight() / 2);

        boolean needDrawUnReachArea = true; // 是否需要绘制未到达进度
        float textWidth = 0;
        String text = mTextPrefix + getProgress() + mTextSuffix;
        if (mTextVisible) {
            textWidth = mTextPaint.measureText(text);
        } else {
            mTextOffset = 0;
        }
        float textHeight = (mTextPaint.descent() + mTextPaint.ascent()) / 2;
        float radio = getProgress() * 1.0f / getMax();
        float progressPosX = (int) (mDrawWidth - textWidth) * (radio);

        if (progressPosX + textWidth >= mDrawWidth) {
            progressPosX = mDrawWidth - textWidth;
            needDrawUnReachArea = false;
        }

        // 绘制已到达进度
        float endX = progressPosX - mTextOffset / 2;
        if (endX > 0) {
            canvas.drawLine(new Point(0, 0), new Point(endX, 0), mReachPaint);
        }

        // 绘制未到达进度
        if (needDrawUnReachArea) {
            float start = progressPosX + mTextOffset / 2 + textWidth;
            canvas.drawLine(new Point(start, 0), new Point(mDrawWidth, 0), mNormalPaint);
        }

        // 绘制字体
        if (!mTextVisible) {
            return;
        }
        switch (mProgressPosition) {
            case BOTTOM: // BOTTOM
                canvas.drawText(mTextPaint, text, progressPosX, -textHeight * 2 + mTextOffset);
                break;
            case TOP: // TOP
                canvas.drawText(mTextPaint, text, progressPosX, 0 - mTextOffset);
                break;
            default: // CENTER
                canvas.drawText(mTextPaint, text, progressPosX, -textHeight);
                break;
        }
    }

    /**
     * Get normal bar size int
     *
     * @return the int
     */
    public int getNormalBarSize() {
        return mNormalBarSize;
    }

    /**
     * Set normal bar size *
     *
     * @param normalBarSize normal bar size
     */
    public void setNormalBarSize(int normalBarSize) {
        mNormalBarSize = Utils.dp2px(getContext(), normalBarSize);
        invalidate();
    }

    /**
     * Get normal bar color int
     *
     * @return the int
     */
    public int getNormalBarColor() {
        return mNormalBarColor;
    }

    /**
     * Set normal bar color *
     *
     * @param normalBarColor normal bar color
     */
    public void setNormalBarColor(int normalBarColor) {
        mNormalBarColor = normalBarColor;
        invalidate();
    }

    /**
     * Get reach bar size int
     *
     * @return the int
     */
    public int getReachBarSize() {
        return mReachBarSize;
    }

    /**
     * Set reach bar size *
     *
     * @param reachBarSize reach bar size
     */
    public void setReachBarSize(int reachBarSize) {
        mReachBarSize = Utils.dp2px(getContext(), reachBarSize);
        invalidate();
    }

    /**
     * Get reach bar color int
     *
     * @return the int
     */
    public int getReachBarColor() {
        return mReachBarColor;
    }

    /**
     * Set reach bar color *
     *
     * @param reachBarColor reach bar color
     */
    public void setReachBarColor(int reachBarColor) {
        mReachBarColor = reachBarColor;
        invalidate();
    }

    /**
     * Get text size int
     *
     * @return the int
     */
    public int getTextSize() {
        return mTextSize;
    }

    /**
     * Set text size *
     *
     * @param textSize text size
     */
    public void setTextSize(int textSize) {
        mTextSize = Utils.sp2px(getContext(), textSize);
        invalidate();
    }

    /**
     * Get text color int
     *
     * @return the int
     */
    public int getTextColor() {
        return mTextColor;
    }

    /**
     * Set text color *
     *
     * @param textColor text color
     */
    public void setTextColor(int textColor) {
        mTextColor = textColor;
        invalidate();
    }

    /**
     * Get text offset int
     *
     * @return the int
     */
    public int getTextOffset() {
        return mTextOffset;
    }

    /**
     * Set text offset *
     *
     * @param textOffset text offset
     */
    public void setTextOffset(int textOffset) {
        mTextOffset = Utils.dp2px(getContext(), textOffset);
        invalidate();
    }

    /**
     * Get progress position int
     *
     * @return the int
     */
    @Position
    public int getProgressPosition() {
        return mProgressPosition;
    }

    /**
     * Set progress position *
     *
     * @param progressPosition progress position
     */
    public void setProgressPosition(@Position int progressPosition) {
        if (progressPosition > 1 || progressPosition < -1) {
            mProgressPosition = 0;
        } else {
            mProgressPosition = progressPosition;
        }
        invalidate();
    }

    /**
     * Is text visible boolean
     *
     * @return the boolean
     */
    public boolean isTextVisible() {
        return mTextVisible;
    }

    /**
     * Set text visible *
     *
     * @param textVisible text visible
     */
    public void setTextVisible(boolean textVisible) {
        mTextVisible = textVisible;
        invalidate();
    }

    /**
     * Get text skew x float
     *
     * @return the float
     */
    public float getTextSkewX() {
        return mTextSkewX;
    }

    /**
     * Set text skew x *
     *
     * @param textSkewX text skew x
     */
    public void setTextSkewX(float textSkewX) {
        mTextSkewX = textSkewX;
        invalidate();
    }

    /**
     * Get text prefix string
     *
     * @return the string
     */
    public String getTextPrefix() {
        return mTextPrefix;
    }

    /**
     * Set text prefix *
     *
     * @param textPrefix text prefix
     */
    public void setTextPrefix(String textPrefix) {
        mTextPrefix = textPrefix;
        invalidate();
    }

    /**
     * Get text suffix string
     *
     * @return the string
     */
    public String getTextSuffix() {
        return mTextSuffix;
    }

    /**
     * Set text suffix *
     *
     * @param textSuffix text suffix
     */
    public void setTextSuffix(String textSuffix) {
        mTextSuffix = textSuffix;
        invalidate();
    }

    /**
     * Run progress anim *
     *
     * @param duration duration
     */
    public void runProgressAnim(long duration) {
        setProgressInTime(0, duration);
    }

    /**
     * Set progress in time *
     *
     * @param progress 进度值
     * @param duration 动画播放时间
     */
    public void setProgressInTime(final int progress, final long duration) {
        setProgressInTime(progress, getProgress(), duration);
    }

    /**
     * Set progress in time *
     *
     * @param startProgress 起始进度
     * @param progress      进度值
     * @param duration      动画播放时间
     */
    public void setProgressInTime(int startProgress, final int progress, final long duration) {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(startProgress, progress);
        valueAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                setProgressValue((int) value);
            }
        });
        valueAnimator.setInterpolator(Animator.CurveType.ACCELERATE_DECELERATE);
        valueAnimator.setDuration(duration);
        valueAnimator.start();
    }

    /**
     * Invalidate
     */
    @Override
    public void invalidate() {
        initPaint();
        super.invalidate();
    }

    /**
     * Position @IntDef({TOP, CENTRE, BOTTOM})
     */
    @Retention(RetentionPolicy.SOURCE)
    public @interface Position {

    }
}
